using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;

namespace Nirvana.Common
{
    public static partial class ReflectionExtend
    {
        public static Type[] GetDefaultChildTypes(this Type type)
        {
            return TypeUtils.defaultTypes.Where(type.IsAssignableFrom).ToArray();
        }

        public static T GetAttribute<T>(this Type type) where T : Attribute
        {
            return (T) type.GetAttribute(typeof(T));
        }

        public static Attribute GetAttribute(this Type type, Type attributeType)
        {
            var attributes = type.GetCustomAttributes(true);
            foreach (Attribute attribute in attributes)
            {
                var attType = attribute.GetType();
                if (attributeType.IsAssignableFrom(attType))
                {
                    return attribute;
                }
            }

            return null;
        }

        public static bool TryGetAttribute<T>(this Type type, out T attribute) where T : Attribute
        {
            attribute = GetAttribute<T>(type);
            return attribute != null;
        }

        public static List<FieldInfo> GetAllFields(this Type type)
        {
            if (_cacheType2FieldInfosDic.ContainsKey(type))
            {
                return _cacheType2FieldInfosDic[type];
            }

            var fieldInfoList = type.GetFields(BindingFlags.Public | BindingFlags.Instance).ToList();
            _cacheType2FieldInfosDic.Add(type, fieldInfoList);
            return fieldInfoList;
        }

        ///<summary>Get a friendly name for the type</summary>
        public static string FriendlyName(this Type t, bool compileSafe = false)
        {
            if (t == null)
            {
                return null;
            }

            if (!compileSafe && t.IsByRef)
            {
                t = t.GetElementType();
            }

            if (!compileSafe && t == typeof(UnityEngine.Object))
            {
                return "UnityObject";
            }

            string s;
            if (!compileSafe && _typeFriendlyName.TryGetValue(t, out s))
            {
                return s;
            }

            if (compileSafe && _typeFriendlyNameCompileSafe.TryGetValue(t, out s))
            {
                return s;
            }

            s = compileSafe ? t.FullName : t.Name;
            if (!compileSafe)
            {
                if (s == "Single")
                {
                    s = "Float";
                }

                if (s == "Single[]")
                {
                    s = "Float[]";
                }

                if (s == "Int32")
                {
                    s = "Integer";
                }

                if (s == "Int32[]")
                {
                    s = "Integer[]";
                }
            }

            if (t.IsGenericParameter)
            {
                s = "T";
            }

            if (t.IsGenericType)
            {
                s = compileSafe && !string.IsNullOrEmpty(t.Namespace) ? t.Namespace + "." + t.Name : t.Name;
                var args = t.NIGetGenericArguments();
                if (args.Length != 0)
                {
                    s = s.Replace("`" + args.Length, "");

                    s += compileSafe ? "<" : " (";
                    for (var i = 0; i < args.Length; i++)
                    {
                        s += (i == 0 ? "" : ", ") + args[i].FriendlyName(compileSafe);
                    }

                    s += compileSafe ? ">" : ")";
                }
            }

            if (compileSafe)
            {
                return _typeFriendlyNameCompileSafe[t] = s;
            }

            return _typeFriendlyName[t] = s;
        }
    }
}